package suncertify.ui;

import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.io.Serializable;
import java.rmi.RemoteException;
import java.util.Vector;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import suncertify.controller.SearchController;
import suncertify.db.RecordNotFoundException;
import suncertify.db.Room;
import suncertify.network.ServerHelper;
import suncertify.network.ServerInterface;
import suncertify.resources.App;

/**
 * The view presented a a user when searching for rooms.  This gives 
 * the user the ability to search by name and/or location.  As of right now, 
 * those are the only two searching options.  The customer can also search 
 * for all objects simply by not entering any criteria into the search boxes.
 * @author Paul Wilhelm
 *
 */
public class SearchGui implements Serializable, Subscriber{

	private static final long serialVersionUID = -8607630402200286067L;				//A UID to be used in deserialization
	private transient JTextField nameTxt;
	private transient JTextField locationTxt;
	private transient JTable resultsTable;
	private transient JFrame frame;
	private transient GridBagConstraints constraint;
	private SearchController controller;						//The controller to handle all the user events
	public static final String[] COLUMN_NAMES = { "Hotel", "City", "Max Occupancy", "Smoking",			//A list of the column names for the JTable
		"Price/Night", "Room Available", "Date"}; 
	private transient ResultsTableModel tableModel;				//The table model to be used for the JTable
	private transient JButton bookBtn;
	private static SearchGui instance;							//An instance of the SearchGui object used in the app (singleton) 							
	private transient JScrollPane jsp;
	private BookGui bookGui;									//The bookGui object to use when the customer click to book a room.

	/**
	 * Constructor.  Creates a new <code>SearchGui</code> object to be displayed at
	 * any given time
	 */
	private SearchGui(){
		controller = new SearchController();
	}
			
	/**
	 * Singleton to make sure that the same object is given to every class.
	 * @return The instance of the <code>SearchGui</code> object
	 */
	public static SearchGui getInstance(){
		if (instance == null){			//If no SearchGui object has been created
			instance = new SearchGui();
		}
		return instance;
	}
	
	/**
	 * Puts all the components together and then shows the GUI.
	 */
	public void buildAndShowGUI() {
		// Set up the frame properties
		constraint = new GridBagConstraints();
		frame = new JFrame();
		frame.setTitle("URLy Bird Hotel Booking");
		frame.setPreferredSize(new Dimension(800, 600));
		frame.setMinimumSize(new Dimension(300, 400));
		frame.setJMenuBar(buildMenuBar());
		if (App.isAlone){
			frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		}
		frame.setIconImage(App.icon);

		// Grab the container element from the frame and add the main panel to
		// it. Then show the frame.
		Container container = frame.getContentPane();
		container.add(buildMainPanel());
		if (App.isNetworked){				//If running in networked mode
			try {
				Subscriber remoteSearchGui = ServerHelper.createRemoteSubscriber(this);
				WindowEventHandler weh = new WindowEventHandler(remoteSearchGui);
				frame.addWindowListener(weh);
				App.localList.add(remoteSearchGui);
				subscribeToChanges(remoteSearchGui);
			} catch (RemoteException re){
				JOptionPane.showMessageDialog(null, "Error exporting the search screen for use with the remote database");
			}
		}
		frame.pack();
		frame.setVisible(true);
		
	}
	
	//Private, not a requirement to document
	private JPanel buildMainPanel() {
		// Set up the main panel which holds all the components on the frame.
		JPanel mainPanel = new JPanel();
		mainPanel.setLayout(new GridBagLayout());

		// Set up the constraints for the description label, then add it to the
		// main panel
		constraint.weightx = 1;
		constraint.fill = GridBagConstraints.HORIZONTAL;
		constraint.insets = new Insets(20, 20, 20, 20);
		mainPanel.add(buildDescriptionText(), constraint);

		// Set up the constraints for the search area, then add it to the panel
		constraint.gridy = 1;
		constraint.insets.left = 100;
		constraint.insets.right = 100;
		mainPanel.add(buildSearchArea(), constraint);

		// Set up the constraints for the results table, then add it to the
		// panel
		constraint.gridy = 2;
		constraint.fill = GridBagConstraints.BOTH;
		constraint.weighty = 1;
		constraint.insets = new Insets(2, 2, 2, 2);
		mainPanel.add(buildResultsTable(), constraint);
		
		//Set up the constraints for the 'Book' button, then add it to the panel
		constraint.gridy = 3;
		constraint.fill = GridBagConstraints.NONE;
		constraint.weighty = 0;
		constraint.anchor = GridBagConstraints.EAST;
		constraint.insets = new Insets(30, 5, 15, 30);
		mainPanel.add(buildBookButton(), constraint);
		return mainPanel;
	}

	//Private, not a requirement to document
	private JPanel buildSearchArea() {
		JPanel searchPanel = new JPanel();
		searchPanel.setLayout(new GridBagLayout());
		
		//Create a local GridBagConstraints to use for the search form
		GridBagConstraints c = new GridBagConstraints();
		
		//Create and set properties for the components of the form
		JLabel nameLbl = new JLabel("Name: ");
		nameTxt = new JTextField();
		JLabel locationLbl = new JLabel("Location: ");
		locationTxt = new JTextField();
		nameTxt.setColumns(20);
		locationTxt.setColumns(20);
		JButton searchBtn = new JButton("Search");
		searchBtn.addActionListener(new ActionListener() {
			/**
			 * {@inheritDoc}
			 */
			public void actionPerformed(ActionEvent e) {
				String name = nameTxt.getText();
				String location = locationTxt.getText();
				resultsTable.clearSelection();
				try{
					Vector<Room> tableData = controller.searchBtnClick(name, location);
					tableModel = new ResultsTableModel(tableData, COLUMN_NAMES);
					resultsTable.setModel(tableModel);
					resultsTable.updateUI();
					resultsTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
						
						/**
						 * {@inheritDoc}
						 */
						public void valueChanged(ListSelectionEvent e) {
							int selectedRow = resultsTable.getSelectedRow();
							if (selectedRow == -1){
								bookBtn.setEnabled(false);
								return;
							}
							if (resultsTable.getValueAt(selectedRow, 5).equals("Yes")){
								bookBtn.setEnabled(true);
							}
							else{
								bookBtn.setEnabled(false);
							}
						}
					});
				} catch (IOException ex){
					ex.printStackTrace();
				} catch (RecordNotFoundException rnfe){ 
					String message = rnfe.getMessage();
					JOptionPane.showMessageDialog(null, message, "Record Not Found", JOptionPane.ERROR_MESSAGE);
				}
			}
		});
		
		//Set up the constraints for the nameLbl
		c.fill = GridBagConstraints.HORIZONTAL;
		searchPanel.add(nameLbl, c);
		
		//Set up the constraints for the nameTxt
		c.weightx = 1;
		c.insets.left = 20;
		searchPanel.add(nameTxt, c);
		
		//Set up the constraints for the locationTxt
		c.weighty = 1;
		c.insets.top = 5;
		c.gridx = 1;
		c.gridy = 1;
		searchPanel.add(locationTxt, c);
		
		//Set up the constraints for the locationLbl
		c.insets.left = 0;
		c.weightx = 0;
		c.gridx = 0;
		c.gridy = 1;
		searchPanel.add(locationLbl, c);
		
		//Set up the constraints for the searchBtn
		c.insets.top = 0;
		c.ipadx = 10;
		c.fill = GridBagConstraints.NONE;
		c.gridy = 2;
		c.gridwidth = 2;
		c.insets.top = 10;
		searchPanel.add(searchBtn, c);
		Dimension d = searchPanel.getPreferredSize();
		d.width = 300;
		searchPanel.setMinimumSize(d);
		Dimension di = searchPanel.getMaximumSize();
		di.width = 600;
		searchPanel.setMaximumSize(di);
		return searchPanel;
	}

	//Private, not a requirement to document
	private JLabel buildDescriptionText() {
		JLabel descriptionLabel = new JLabel(
				"<HTML><CENTER>Please search for a hotel by either name and/or location."
						+ "  Clicking search with no criteria entered will return all results.</CENTER></HTML>");
		return descriptionLabel;
	}

	//Private, not a requirement to document
	private JScrollPane buildResultsTable() {
		resultsTable = new JTable(new String[50][7], COLUMN_NAMES);
		resultsTable.setAutoCreateRowSorter(true);
		resultsTable.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
		resultsTable.getTableHeader().setReorderingAllowed(false);
		jsp = new JScrollPane(resultsTable);
		return jsp;
	}

	//Private, not a requirement to document
	private JMenuBar buildMenuBar() {
		JMenuBar menuBar = new JMenuBar();
		JMenu fileMenu = new JMenu("File");

		// Setup the sub-menus
		JMenuItem exitMenuItem = new JMenuItem("Exit");
		exitMenuItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				if (App.isNetworked){	//If networked, dispose the frame to run the Window Event Handler
					frame.dispose();
				}
				if (App.isAlone){		//If running in alone mode, just quit.
					System.exit(0);
				}
			}
		});
		fileMenu.add(exitMenuItem);

		// Add item/s to the MenuBar
		menuBar.add(fileMenu);
		return menuBar;
	}
	
	//Private, not a requirement to document
	private JButton buildBookButton(){
		bookBtn = new JButton("Book Room");
		bookBtn.setEnabled(false);
		bookBtn.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				int selectedRow = resultsTable.getSelectedRow();
				String name = (String) resultsTable.getValueAt(selectedRow, 0);
				String location = (String) resultsTable.getValueAt(selectedRow, 1);
				if (bookGui == null || !bookGui.getFrame().isVisible()){
					bookGui = new BookGui(name, location);
					bookGui.buildAndShowGui();
				}
				else {
					JOptionPane.showMessageDialog(null, "There may only be one book window open at a time");
				}
			}
		});
		return bookBtn;
	}
	
	/**
	 * Gets the <code>JTable</code> containing the results.
	 * @return The JTable object containing the results
	 */
	public JTable getResultsTable(){
		return resultsTable;
	}
	
	/**
	 * Gets the <code>AbstractTableModel</code> used for the <code>JTable</code> element.
	 * @return The table model used
	 */
	public ResultsTableModel getTableModel(){
		return tableModel;
	}
	
	/**
	 * {@inheritDoc}
	 */
	public void update(String requestorName, String requestorLocation) {
		String name = nameTxt.getText();
		String location = locationTxt.getText();
		resultsTable.clearSelection();
		try{
			Vector<Room> tableData = controller.searchBtnClick(name, location);
			tableModel = new ResultsTableModel(tableData, COLUMN_NAMES);
			resultsTable.setModel(tableModel);
			resultsTable.updateUI();
			
		} catch (IOException ex){
			ex.printStackTrace();
		} catch (RecordNotFoundException rnfe){ 
			String message = rnfe.getMessage();
			JOptionPane.showMessageDialog(null, message, "Record Not Found", JOptionPane.ERROR_MESSAGE);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	public void subscribeToChanges(Subscriber s) {
		try {
			ServerInterface data = ServerHelper.getRemoteDatabase();
			data.subscribe(s);
		} catch (RemoteException re){
			JOptionPane.showMessageDialog(null, "Unable to subscribe the view to data changes");
		}
	}
	
	/**
	 * Gets the name text value from the nameTxt <code>JTextField</code>.
	 * @return The nameTxt's value
	 */
	public String getName(){
		return nameTxt.getText();
	}
	
	/**
	 * Gets the location value from the locationTxt <code>JTextField</code>.
	 * @return The locationTxt's value
	 */
	public String getLocation(){
		return locationTxt.getText();
	}
	
	/**
	 * Gets the title of the frame.  This is used to unsubscribe this view from data changes.
	 * @return The title of the frame
	 */
	public String getTitle(){
		return frame.getTitle();
	}
	
}